package com.imooc.ad.handler;

import com.alibaba.fastjson.JSON;
import com.imooc.ad.dump.table.AdCreativeTable;
import com.imooc.ad.dump.table.AdCreativeUnitTable;
import com.imooc.ad.dump.table.AdPlanTable;
import com.imooc.ad.dump.table.AdUnitDistrictTable;
import com.imooc.ad.dump.table.AdUnitItTable;
import com.imooc.ad.dump.table.AdUnitKeywordTable;
import com.imooc.ad.dump.table.AdUnitTable;
import com.imooc.ad.index.DataTable;
import com.imooc.ad.index.IndexAware;
import com.imooc.ad.index.adplan.AdPlanIndex;
import com.imooc.ad.index.adplan.AdPlanObject;
import com.imooc.ad.index.adunit.AdUnitIndex;
import com.imooc.ad.index.adunit.AdUnitObject;
import com.imooc.ad.index.creative.CreativeIndex;
import com.imooc.ad.index.creative.CreativeObject;
import com.imooc.ad.index.creativeunit.CreativeUnitIndex;
import com.imooc.ad.index.creativeunit.CreativeUnitObject;
import com.imooc.ad.index.district.UnitDistrictIndex;
import com.imooc.ad.index.interest.UnitItIndex;
import com.imooc.ad.index.keyword.UnitKeywordIndex;
import com.imooc.ad.mysql.constant.OpType;
import com.imooc.ad.utils.CommonUtils;
import lombok.extern.slf4j.Slf4j;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * 1. 索引之间存在着层级的划分, 也就是依赖关系的划分
 * 2. 加载全量索引其实是增量索引 "添加" 的一种特殊实现
 *  AdPlan -> AdPlanTable -> AdPlanIndex -> AdPlanObject
 *  表中数据 -> 导出数据 -> 读取数据文件，调用索引的CRUD方法 -> 构造索引服务中表对应索引对象(全量/增量索引)
 *  实现“构造索引服务中表对应索引对象(全量/增量索引)”的索引操作处理类
 */
@Slf4j
public class AdLevelDataHandler {

    //监听binlog对增量索引CRUD，以及加载全量索引
    //根据类型type，使用IndexAware中CRUD方法，构造索引对象
    private static <K, V> void handleBinlogEvent(IndexAware<K, V> index, K key, V value, OpType type) {

        switch (type) {
            case ADD:
                index.add(key, value);
                break;
            case UPDATE:
                index.update(key, value);
                break;
            case DELETE:
                index.delete(key, value);
                break;
            default:
                break;
        }
    }

    //加载第二层级全量索引，计划表AdPlanTable -> AdPlanObject
    public static void handleLevel2(AdPlanTable planTable, OpType type) {

        AdPlanObject planObject = new AdPlanObject(
                planTable.getId(), planTable.getUserId(), planTable.getPlanStatus(),
                planTable.getStartDate(), planTable.getEndDate()
        );
        //构造AdPlanObject索引对象
        handleBinlogEvent(DataTable.of(AdPlanIndex.class), planObject.getPlanId(), planObject, type);
    }

    //加载第二层级全量索引，创意表AdCreativeTable -> CreativeObject
    public static void handleLevel2(AdCreativeTable creativeTable, OpType type) {

        CreativeObject creativeObject = new CreativeObject(
                creativeTable.getAdId(), creativeTable.getName(), creativeTable.getType(), creativeTable.getMaterialType(),
                creativeTable.getHeight(), creativeTable.getWidth(), creativeTable.getAuditStatus(), creativeTable.getAdUrl()
        );
        //构造CreativeObject索引对象
        handleBinlogEvent(DataTable.of(CreativeIndex.class), creativeObject.getAdId(), creativeObject, type);
    }

    //加载第三层级全量索引，依赖于第二层级AdPlanTable，广告单元表AdUnitTable -> AdPlanObject
    public static void handleLevel3(AdUnitTable unitTable, OpType type) {

        //判断依赖的上层索引对象是否存在
        AdPlanObject adPlanObject = DataTable.of(AdPlanIndex.class).get(unitTable.getPlanId());
        if (adPlanObject == null) {
            log.error("handleLevel3 found AdPlanObject error: {}", unitTable.getPlanId());
            return;
        }

        AdUnitObject unitObject = new AdUnitObject(
                unitTable.getUnitId(), unitTable.getUnitStatus(), unitTable.getPositionType(),
                unitTable.getPlanId(), adPlanObject
        );
        //构造AdUnitObject索引对象
        handleBinlogEvent(DataTable.of(AdUnitIndex.class), unitTable.getUnitId(), unitObject, type);
    }

    //加载第三层级全量索引，依赖于广告单元和创意，广告单元创意表AdCreativeUnitTable -> CreativeUnitObject
    public static void handleLevel3(AdCreativeUnitTable creativeUnitTable, OpType type) {

        //不支持更新
        if (type == OpType.UPDATE) {
            log.error("CreativeUnitIndex not support update");
            return;
        }

        //判断依赖的上层索引对象是否存在
        AdUnitObject unitObject = DataTable.of(AdUnitIndex.class).get(creativeUnitTable.getUnitId());
        CreativeObject creativeObject = DataTable.of(CreativeIndex.class).get(creativeUnitTable.getAdId());
        if (null == unitObject || null == creativeObject) {
            log.error("AdCreativeUnitTable index error: {}", JSON.toJSONString(creativeUnitTable));
            return;
        }
        //构造CreativeUnitObject索引对象
        CreativeUnitObject creativeUnitObject = new CreativeUnitObject(creativeUnitTable.getAdId(), creativeUnitTable.getUnitId());
        // <adId-unitId, CreativeUnitObject>，adId-unitId作为key确认唯一的广告单元与创意关联关系
        String key = CommonUtils.stringConcat(creativeUnitObject.getAdId().toString(), creativeUnitObject.getUnitId().toString());
        handleBinlogEvent(DataTable.of(CreativeUnitIndex.class), key, creativeUnitObject, type);
    }

    //加载第四层级全量索引，依赖于第三层级广告单元，广告单元地域表AdUnitDistrictTable -> UnitDistrictIndex中districtUnitMap
    public static void handleLevel4(AdUnitDistrictTable unitDistrictTable, OpType type) {

        //不支持更新
        if (type == OpType.UPDATE) {
            log.error("district index can not support update");
            return;
        }

        //判断依赖的上层索引对象是否存在
        AdUnitObject unitObject = DataTable.of(AdUnitIndex.class).get(unitDistrictTable.getUnitId());
        if (unitObject == null) {
            log.error("AdUnitDistrictTable index error: {}", unitDistrictTable.getUnitId());
            return;
        }

        //将省市用连字符连接
        String key = CommonUtils.stringConcat(unitDistrictTable.getProvince(), unitDistrictTable.getCity());
        //UnitDistrictIndex中districtUnitMap中对应的value单元ID是set类型，将unitDistrictTable获取的单个ID存入set
        Set<Long> value = new HashSet<>(Collections.singleton(unitDistrictTable.getUnitId()));
        handleBinlogEvent(DataTable.of(UnitDistrictIndex.class), key, value, type);
    }

    //加载第四层级全量索引，依赖于第三层级广告单元，广告单元兴趣表AdUnitItTable -> UnitItIndex中itUnitMap
    public static void handleLevel4(AdUnitItTable unitItTable, OpType type) {

        //不支持更新
        if (type == OpType.UPDATE) {
            log.error("it index can not support update");
            return;
        }

        //判断依赖的上层索引对象是否存在
        AdUnitObject unitObject = DataTable.of(AdUnitIndex.class).get(unitItTable.getUnitId());
        if (unitObject == null) {
            log.error("AdUnitItTable index error: {}", unitItTable.getUnitId());
            return;
        }

        //UnitItIndex中itUnitMap中对应的value单元ID是set类型，将unitItTable获取的单个ID存入set
        Set<Long> value = new HashSet<>(Collections.singleton(unitItTable.getUnitId()));
        handleBinlogEvent(DataTable.of(UnitItIndex.class), unitItTable.getItTag(), value, type);
    }

    //加载第四层级全量索引，依赖于第三层级广告单元，广告单元关键字表AdUnitKeywordTable -> UnitKeywordIndex中itUnitMap
    public static void handleLevel4(AdUnitKeywordTable keywordTable, OpType type) {

        //不支持更新
        if (type == OpType.UPDATE) {
            log.error("keyword index can not support update");
            return;
        }

        //判断依赖的上层索引对象是否存在
        AdUnitObject unitObject = DataTable.of(AdUnitIndex.class).get(keywordTable.getUnitId());
        if (unitObject == null) {
            log.error("AdUnitKeywordTable index error: {}", keywordTable.getUnitId());
            return;
        }

        //UnitKeywordIndex中keywordUnitMap中对应的value单元ID是set类型，将keywordTable获取的单个ID存入set
        Set<Long> value = new HashSet<>(Collections.singleton(keywordTable.getUnitId()));
        handleBinlogEvent(DataTable.of(UnitKeywordIndex.class), keywordTable.getKeyword(), value, type);
    }

}
